﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Threading;

namespace ControlLibrary.Controls;

/// <summary>
///     自定义日期时间选择器控件
/// </summary>
[TemplatePart(Name = "PART_StartCalendar", Type = typeof(Calendar))]
[TemplatePart(Name = "PART_ClearButton", Type = typeof(Button))]
[TemplatePart(Name = "PART_ConfirmButton", Type = typeof(Button))]
[TemplatePart(Name = "PART_OpenButton", Type = typeof(Button))]
[TemplatePart(Name = "PART_DateTimeRanger", Type = typeof(TextBox))]
public class DateTimePicker : Control
{
    // 开始日历控件
    private Calendar? _startCalendar;

    static DateTimePicker()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(DateTimePicker),
            new FrameworkPropertyMetadata(typeof(DateTimePicker)));
    }

    /// <summary>
    ///     当控件应用模板时调用
    /// </summary>
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        // 获取模板部件
        _startCalendar = GetTemplateChild("PART_StartCalendar") as Calendar;

        // 挂接事件处理程序
        if (_startCalendar != null)
        {
            _startCalendar.PreviewMouseUp += Calendar_PreviewMouseUp;
        }


        // 绑定按钮事件
        if (GetTemplateChild("PART_ClearButton") is Button clearButton)
            clearButton.Click += ClearButton_Click;

        if (GetTemplateChild("PART_ConfirmButton") is Button confirmButton)
            confirmButton.Click += ConfirmButton_Click;

        if (GetTemplateChild("PART_DateTimeRanger") is TextBox dateTimeRanger)
            dateTimeRanger.GotMouseCapture += (_, _) => OpenPicker();

        if (GetTemplateChild("PART_OpenButton") is Button openButton)
            openButton.Click += (_, _) => OpenPicker();

        UpdateBlackoutDates();
    }

    /// <summary>
    ///     日历预览鼠标抬起事件处理
    /// </summary>
    private static void Calendar_PreviewMouseUp(object sender, MouseButtonEventArgs e)
    {
        if (Mouse.Captured is CalendarItem)
        {
            Mouse.Capture(null);
        }
    }


    private static void OnSelectedDateTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is not DateTimePicker picker) return;

        if (e.NewValue is not DateTime date) return;
        var newDate = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, 0);
        if (date == newDate) return;
        picker.SetCurrentValue(e.Property, newDate);
        var bindingExpression = BindingOperations.GetBindingExpression(d, e.Property);
        if (bindingExpression is not null)
        {
            picker.Dispatcher.BeginInvoke(DispatcherPriority.DataBind,
                new Action(() => { bindingExpression.UpdateSource(); }));
        }
    }


    /// <summary>
    ///     设置最小最大值属性的回调方法
    /// </summary>
    private static void OnMinMaxDateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is DateTimePicker picker)
            picker.UpdateBlackoutDates();
    }

    /// <summary>
    ///     更新不可选择的日期
    /// </summary>
    private void UpdateBlackoutDates()
    {
        if (_startCalendar == null) return;
        var min = Min(MinDate, MaxDate);
        var max = Max(MinDate, MaxDate);
        _startCalendar.BlackoutDates.Clear();
        // 设置最小日期之前的日期为不可选
        if ((min - DateTime.MinValue).TotalDays > 1)
        {
            _startCalendar.BlackoutDates.Add(new CalendarDateRange(DateTime.MinValue, min.AddDays(-1)));
        }

        // 设置最大日期之后的日期为不可选
        if ((DateTime.MaxValue - max).TotalDays < 1) return;
        _startCalendar.BlackoutDates.Add(new CalendarDateRange(max.AddDays(1), DateTime.MaxValue));
    }

    private static DateTime Min(DateTime a, DateTime b)
    {
        return a > b ? b : a;
    }

    private static DateTime Max(DateTime a, DateTime b)
    {
        return a > b ? a : b;
    }

    /// <summary>
    ///     确认按钮点击事件处理
    /// </summary>
    private void ConfirmButton_Click(object sender, RoutedEventArgs e)
    {
        if (TempSelectedDate is null) return;
        SelectedDateTime = CombineDateTime(TempSelectedDate.Value, HourIndex, MinuteIndex);
        IsPopupOpen = false;
    }

    /// <summary>
    ///     组合日期时间
    /// </summary>
    private static DateTime CombineDateTime(DateTime date, int hours, int minutes)
    {
        return date.Date.AddHours(hours).AddMinutes(minutes);
    }

    /// <summary>
    ///     打开日期选择器
    /// </summary>
    private void OpenPicker()
    {
        TempSelectedDate = SelectedDateTime.Date;
        HourIndex = SelectedDateTime.Hour;
        MinuteIndex = SelectedDateTime.Minute;

        if (_startCalendar != null)
            _startCalendar.DisplayDate = SelectedDateTime;

        IsPopupOpen = true;
    }

    /// <summary>
    ///     清除按钮点击事件处理
    /// </summary>
    private void ClearButton_Click(object sender, RoutedEventArgs e)
    {
        SelectedDateTime = DateTime.Now;
        IsPopupOpen = false;
    }

    #region 依赖属性

    /// <summary>
    ///     选中的日期时间属性
    /// </summary>
    public static readonly DependencyProperty SelectedDateTimeProperty = DependencyProperty.Register(
        nameof(SelectedDateTime), typeof(DateTime), typeof(DateTimePicker),
        new FrameworkPropertyMetadata(DateTime.MinValue, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
            OnSelectedDateTimeChanged));

    /// <summary>
    ///     最小值属性
    /// </summary>
    public static readonly DependencyProperty MinDateProperty =
        DependencyProperty.Register(nameof(MinDate), typeof(DateTime), typeof(DateTimePicker),
            new PropertyMetadata(DateTime.MinValue, OnMinMaxDateChanged));

    /// <summary>
    ///     最大值属性
    /// </summary>
    public static readonly DependencyProperty MaxDateProperty =
        DependencyProperty.Register(nameof(MaxDate), typeof(DateTime), typeof(DateTimePicker),
            new PropertyMetadata(DateTime.MaxValue, OnMinMaxDateChanged));

    /// <summary>
    ///     弹出框是否打开属性
    /// </summary>
    internal static readonly DependencyProperty IsPopupOpenProperty =
        DependencyProperty.Register(nameof(IsPopupOpen), typeof(bool), typeof(DateTimePicker),
            new PropertyMetadata(false));

    /// <summary>
    ///     开始小时索引属性
    /// </summary>
    internal static readonly DependencyProperty HourIndexProperty =
        DependencyProperty.Register(nameof(HourIndex), typeof(int), typeof(DateTimePicker),
            new PropertyMetadata(0));

    /// <summary>
    ///     开始分钟索引属性
    /// </summary>
    internal static readonly DependencyProperty MinuteIndexProperty =
        DependencyProperty.Register(nameof(MinuteIndex), typeof(int), typeof(DateTimePicker),
            new PropertyMetadata(0));

    /// <summary>
    ///     临时选择的日期属性
    /// </summary>
    internal static readonly DependencyProperty TempSelectedDateProperty =
        DependencyProperty.Register(nameof(TempSelectedDate), typeof(DateTime?), typeof(DateTimePicker),
            new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

    #endregion

    #region 属性访问器

    /// <summary>
    ///     选中的日期时间
    /// </summary>
    public DateTime SelectedDateTime
    {
        get => (DateTime)GetValue(SelectedDateTimeProperty);
        set => SetValue(SelectedDateTimeProperty, value);
    }

    /// <summary>
    ///     最小可选日期
    /// </summary>
    public DateTime MinDate
    {
        get => (DateTime)GetValue(MinDateProperty);
        set => SetValue(MinDateProperty, value);
    }

    /// <summary>
    ///     最大可选日期
    /// </summary>
    public DateTime MaxDate
    {
        get => (DateTime)GetValue(MaxDateProperty);
        set => SetValue(MaxDateProperty, value);
    }

    /// <summary>
    ///     弹出框是否打开
    /// </summary>
    internal bool IsPopupOpen
    {
        get => (bool)GetValue(IsPopupOpenProperty);
        set => SetValue(IsPopupOpenProperty, value);
    }

    /// <summary>
    ///     小时索引
    /// </summary>
    internal int HourIndex
    {
        get => (int)GetValue(HourIndexProperty);
        set => SetValue(HourIndexProperty, value);
    }

    /// <summary>
    ///     分钟索引
    /// </summary>
    internal int MinuteIndex
    {
        get => (int)GetValue(MinuteIndexProperty);
        set => SetValue(MinuteIndexProperty, value);
    }

    /// <summary>
    ///     临时选择的日期
    /// </summary>
    internal DateTime? TempSelectedDate
    {
        get => (DateTime?)GetValue(TempSelectedDateProperty);
        set => SetValue(TempSelectedDateProperty, value);
    }

    #endregion
}